﻿/******************************************************************************
 *  Copyright 2013 Bernhard Grünewaldt                                        *
 *                                                                            *
 *  Licensed under the Apache License, Version 2.0 (the "License");           *
 *  you may not use this file except in compliance with the License.          *
 *  You may obtain a copy of the License at                                   *
 *                                                                            *
 *      http://www.apache.org/licenses/LICENSE-2.0                            *
 *                                                                            *
 *  Unless required by applicable law or agreed to in writing, software       *
 *  distributed under the License is distributed on an "AS IS" BASIS,         *
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  *
 *  See the License for the specific language governing permissions and       *
 *  limitations under the License.                                            *
 ******************************************************************************/
package com.google.code.greenwood.confluencemavenreleaseinfomacro.macro;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.Map;

import org.apache.commons.httpclient.HttpException;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;

import com.atlassian.cache.Cache;
import com.atlassian.cache.CacheManager;
import com.atlassian.confluence.content.render.xhtml.ConversionContext;
import com.atlassian.confluence.macro.Macro;
import com.atlassian.confluence.macro.MacroExecutionException;
import com.atlassian.renderer.RenderContext;
import com.atlassian.renderer.v2.RenderMode;
import com.atlassian.renderer.v2.macro.BaseMacro;
import com.atlassian.renderer.v2.macro.MacroException;
import com.google.code.greenwood.confluencemavenreleaseinfomacro.parser.DomDocumentToReleaseInfoParser;
import com.google.code.greenwood.confluencemavenreleaseinfomacro.parser.HttpReleaseInfoDownloader;
import com.google.code.greenwood.confluencemavenreleaseinfomacro.pojo.ReleaseInformation;
import com.google.code.greenwood.confluencemavenreleaseinfomacro.pojo.StaticClassWrapper;
import com.google.code.greenwood.confluencemavenreleaseinfomacro.transformer.ReleaseInformationHtmlTransformer;


public class ReleaseInfoMacro extends BaseMacro implements Macro {
	
	private static final Logger log = LoggerFactory.getLogger(ReleaseInfoMacro.class);

	// mockable
	private StaticClassWrapper staticClassWrapper;
	// mockable
	private ReleaseInformationHtmlTransformer htmltransformer;
	
	// SpringDI by Confluence
	private CacheManager cacheManager;
	public final static String RELEASEINFO_MACRO_CACHE_KEY = "confluencemavenreleaseinfomacro";
	// cache expires after 15 mins
	private long cacheTime = 15*60;
	
	public final static String urlToReleasePomXmlKey = "urlToReleasePomXml";
	
	public final static String baseUrlToMavenRepoKey = "baseUrlToMavenRepo";

	public final static String httpUsernameKey = "httpUsername";
	
	public final static String httpPasswordKey = "httpPassword";
	
	
	private static final String errorHtmlPrefix = "<div style=\"border:1px solid #e02828;color:#e02828;font-weight:bold;padding:6px;background-color:#ffdede;\">";
	private static final String errorHtmlSuffix = "</div>";

	private static final String infoHtmlPrefix = "<div style=\"border:1px solid #2445bd;color:#2445bd;font-weight:bold;padding:6px;background-color:#dee5ff;\">";
	private static final String infoHtmlSuffix = "</div>";
	
	@Override
	public String execute(Map<String, String> params, String body, ConversionContext ctx) throws MacroExecutionException {
		String httpUsername = null;
		String httpPassword = null;
		String baseUrlToMavenRepo = null;
		String urlToReleasePomXml = null;

		if (staticClassWrapper == null) staticClassWrapper = new StaticClassWrapper();
		
		
		Cache cache = cacheManager.getCache(RELEASEINFO_MACRO_CACHE_KEY);
		
		
		if (params.containsKey(urlToReleasePomXmlKey)) {
        	urlToReleasePomXml = params.get(urlToReleasePomXmlKey);
        }
        if (params.containsKey(baseUrlToMavenRepoKey)) {
        	baseUrlToMavenRepo = params.get(baseUrlToMavenRepoKey);
        }
        if (params.containsKey(httpUsernameKey)) {
        	httpUsername = params.get(httpUsernameKey);
        }
        if (params.containsKey(httpPasswordKey)) {
        	httpPassword = params.get(httpPasswordKey);
        }
        
		String innerKeyCacheName;
		try {
			innerKeyCacheName = ReleaseInfoMacroCacheObject.generateCacheKey(urlToReleasePomXml, baseUrlToMavenRepo, httpUsername, httpPassword);
		} catch (NoSuchAlgorithmException e1) {
			throw new MacroExecutionException(e1);
		}

        
        String bodycontent = null;
        if (urlToReleasePomXml != null) {
			try {
				//log.debug((String)cache.get(innerKeyCacheName));
				ReleaseInfoMacroCacheObject cacheObj = null;
				try {
					cacheObj = (ReleaseInfoMacroCacheObject) cache.get(innerKeyCacheName);
				} catch (ClassCastException e) {
					// If ReleaseInfoMacroCacheObject of old version with different footprint is already there, discard the object.
					System.out.println("ClassCastException gefangen. Cache wird invalidiert."+e.getMessage());
					cacheObj = null;
				}
				if (cacheObj != null) {
					// invalidate Cache after 15 minutes
					Date now = staticClassWrapper.newDate();
					log.debug("CacheTimes: now: "+now.toString()+" ; cache:"+cacheObj.getCreated().toString());
					long diff = now.getTime() - cacheObj.getCreated().getTime();
					long diffSeconds = diff / 1000 % 60;  
					log.debug("cache diff seconds: "+diffSeconds);
					if (diffSeconds < cacheTime) {
						log.debug("got content from cache");
						bodycontent = cacheObj.getContent();
						return bodycontent;
					}
				} else {
					log.debug("cache is null!");
				}
				log.debug("Recreating cache.");
				HttpReleaseInfoDownloader down = new HttpReleaseInfoDownloader();
				File parentpom = down.downloadMavenReleaseRecursively(new URL(urlToReleasePomXml), httpUsername,httpPassword);
				
				DomDocumentToReleaseInfoParser parser = new DomDocumentToReleaseInfoParser();
				ReleaseInformation info = parser.parsePomFilesRecursive(parentpom, true);
				
				
				
				// cleanup 
				FileUtils.deleteDirectory(parentpom.getParentFile());
				
				Date lastCacheUpdate = staticClassWrapper.newDate();
				info.setLastCacheUpdate(lastCacheUpdate);
				if (htmltransformer == null) htmltransformer = new ReleaseInformationHtmlTransformer();
				bodycontent = htmltransformer.toHtml(info, baseUrlToMavenRepo, urlToReleasePomXml);
				ReleaseInfoMacroCacheObject rimcache = new ReleaseInfoMacroCacheObject();
				rimcache.setContent(bodycontent);
				rimcache.setCreated(lastCacheUpdate);
				
				cache.put(innerKeyCacheName, rimcache);
			
				return bodycontent;
	
			} catch (SAXException e) {
				System.err.println("SAXException: "+e.getMessage());
				return errorHtmlPrefix+staticClassWrapper.confluenceActionSupport_getText("releaseinfomacro.exceptiontext.saxexception")+errorHtmlSuffix;
			} catch (MalformedURLException e) {
				System.err.println("MalformedURLException: "+e.getMessage());
				return errorHtmlPrefix+staticClassWrapper.confluenceActionSupport_getText("releaseinfomacro.exceptiontext.malformedurlexception")+errorHtmlSuffix;
			} catch (HttpException e) {
				System.err.println("HttpException: "+e.getMessage());
				return errorHtmlPrefix+staticClassWrapper.confluenceActionSupport_getText("releaseinfomacro.exceptiontext.httpexception", new String[]{e.getMessage()})+errorHtmlSuffix;
			} catch (IOException e) {
				System.err.println("IOException: "+e.getMessage());
				return errorHtmlPrefix+staticClassWrapper.confluenceActionSupport_getText("releaseinfomacro.exceptiontext.ioexception", new String[]{e.getMessage()})+errorHtmlSuffix;
			} catch(Exception e) {
				System.err.println("Exception: "+e.getMessage());
				throw new MacroExecutionException(e);			
			}
        }		
        return  infoHtmlPrefix+staticClassWrapper.confluenceActionSupport_getText("releaseinfomacro.norurl.specified")+infoHtmlSuffix;
	}


	@Override
	public BodyType getBodyType() {
		return BodyType.NONE;
	}


	@Override
	public OutputType getOutputType() {
		return OutputType.INLINE;
	}
	
	@SuppressWarnings("unchecked")
	@Override
    public String execute(@SuppressWarnings("rawtypes") Map params, String body, RenderContext renderContext) throws MacroException
    {
        try
        {
            return execute(params, body, (ConversionContext) null);
        }
        catch(MacroExecutionException e)
        {
            throw new MacroException(e);
        }
    }


	@Override
	public RenderMode getBodyRenderMode() {
		return RenderMode.INLINE;
	}


	@Override
	public boolean hasBody() {
		return false;
	}



	public void setCacheManager(CacheManager cacheManager) {
		this.cacheManager = cacheManager;
	}


	public void setCacheTime(long cacheTime) {
		this.cacheTime = cacheTime;
	}


	public void setStaticClassWrapper(StaticClassWrapper staticClassWrapper) {
		this.staticClassWrapper = staticClassWrapper;
	}


	public void setHtmltransformer(ReleaseInformationHtmlTransformer htmltransformer) {
		this.htmltransformer = htmltransformer;
	}



	
	
	

	
    
}
